package gsd.gaudi.client.ripper;

import gsd.gaudi.common.GaudiGuiHierarchy;
import gsd.gaudi.common.model.GaudiEventModel;
import gsd.gaudi.common.model.GaudiObject;
import gsd.gaudi.common.model.structure.GaudiGuiStructure;

import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.Container;
import java.awt.Window;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.EventListener;
import java.util.LinkedList;

import javax.accessibility.AccessibleAction;
import javax.accessibility.AccessibleContext;
import javax.swing.JMenu;
import javax.swing.JMenuItem;

public class GaudiRipper {

	private String filename;
	
	private GaudiGuiHierarchy hierarchy;
	private GaudiGuiStructure structure;
	private RipperMonitor listener;
	
	private LinkedList<Window> ripped_windows;
	private Window currentWindow;
	private AWTEvent lastEvent;
	private String lastWidgetClicked = "";
	
	public GaudiRipper(String file) {
		this.filename = file;
		this.hierarchy = new GaudiGuiHierarchy();
		this.structure = new GaudiGuiStructure();
	}
	
	private void setListener(RipperMonitor ripperMonitor) {
		this.listener = ripperMonitor;
	}

	public void rip() {
		
		try {
			Thread.sleep(4000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		this.hierarchy.processGUI();
		this.processGUI();
		this.setListener(new RipperMonitor(this));
	}

	public void processGUI(){
		Component[] rootWindows = Window.getWindows();
		this.processChildren(rootWindows);
	}
	
	public void processWidget(Component w){
		
		String id = this.hierarchy.findWidgetId(w);
		String pid = "";
		if(w instanceof JMenuItem){
			pid = id.substring(0, id.lastIndexOf("/"));
		}
		else
			pid = this.hierarchy.findWidgetId(w.getParent());
		
		System.out.println("[GaudiRipper]-->Ripping: " + w.getClass().getCanonicalName() + " " + id);
		
		GaudiObject o = this.extractComponentInfo(w, id, pid);
		this.structure.addWidget(o, id, pid);
		
		if(w instanceof Container){
			Container c = (Container)w;
			this.processChildren(c.getComponents());
		}
		if(w instanceof JMenu){
			JMenu c = (JMenu)w;
			this.processChildren(c.getMenuComponents());
		}
	}
	
	public void processWidgetInvokedBy(Component w, String pid){
		
		String id = this.hierarchy.findWidgetId(w);
		
		System.out.println("[GaudiRipper]-->Ripping: " + w.getClass().getCanonicalName() + " " + id);
		
		GaudiObject o = this.extractComponentInfo(w, id, pid);
		this.structure.addWidget(o, id, pid);
		this.structure.addOpenWindowEvent(pid, id);
		
		if(w instanceof Container){
			Container c = (Container)w;
			this.processChildren(c.getComponents());
		}
		if(w instanceof JMenu){
			JMenu c = (JMenu)w;
			this.processChildren(c.getMenuComponents());
		}
	}
	
	private GaudiObject extractComponentInfo(Component w, String id, String pid) {

		GaudiObject o = new GaudiObject(id);
		
		AccessibleContext context = w.getAccessibleContext();
		
		//System.out.println(">> widget name" + w.getName());
		//System.out.println(">> class name" + w.getClass().getCanonicalName());
		
		o.addProperties("class", w.getClass().getCanonicalName());
		//o.addProperties("x", w.getX());
		//o.addProperties("y", w.getY());
		//o.addProperties("font", w.getFont().toString());
		//o.addProperties("heigth", w.getHeight());
		//o.addProperties("width", w.getWidth());
		
		if(context != null){
			
			//Check for possible click events
			AccessibleAction aAction = context.getAccessibleAction();
			if(aAction != null){
				
				GaudiEventModel ev = new GaudiEventModel(id, GaudiEventModel.mouseEvent);
				o.addEvent(ev);
				
				EventListener[] array = w.getListeners(ActionListener.class);
				int isize = array.length;
				for(int u = 0; u < isize; u++){
					
					System.out.println("--Listener: " + array[u].getClass().getName() + "/actionPerformed");
					ev.getListeners().add(array[u].getClass().getName() + "/actionPerformed");
				
				}
			}
			
			//Check for other kinds of events
			
		}

		return o;
	}

	public void processChildren(Component[] array){
		for(int i = 0; i < array.length; i++){
			this.processWidget(array[i]);
		}
	}
	
	public void recordGuiStructure(){
		
		System.out.println("[GaudiRipper]-Recording structure to file...");
		
		(new File("gaudi")).mkdir();
		(new File("./gaudi/structure")).mkdir();
		
		String filename = "./gaudi/structure/" + this.filename + "Model.txt";
		
		try{
			OutputStream file = new FileOutputStream(filename);
		    OutputStream buffer = new BufferedOutputStream(file);
		    ObjectOutput output = new ObjectOutputStream(buffer);
		    output.writeObject(this.structure);
			output.close();
		}catch (Exception e){
			System.err.println("Error: " + e.getMessage());
		}
		
		System.out.println("[GaudiRipper]-Structure recorded in file: " + filename);
		System.out.println("[GaudiRipper]-Ripped " + this.structure.getK2W().entrySet().size() + " widgets.");
		System.out.println("[GaudiRipper]-Ripped " + this.structure.getLister2widget().size() + " listeners.");
	}


	public void printStructure(){
		this.structure.printMe();
	}
	
	public void processEvent(AWTEvent arg0) {
		
		if(arg0.getID() == WindowEvent.WINDOW_ACTIVATED && !this.lastWidgetClicked.equals("")){	
			if (this.hierarchy.findWidgetId((Component) arg0.getSource()) == null &&
				this.hierarchy.findWidget(this.lastWidgetClicked + "/0") == null){
				
				this.hierarchy.processWidget((Component) arg0.getSource(), this.lastWidgetClicked + "/0");
				this.processWidgetInvokedBy((Component) arg0.getSource(), this.lastWidgetClicked);
				
			}else if (this.hierarchy.findWidgetId((Component) arg0.getSource()) == null &&
					  this.hierarchy.findWidget(this.lastWidgetClicked + "/0") != null){
				
				this.hierarchy.updateWidget((Component) arg0.getSource(), this.lastWidgetClicked + "/0");
			}
		}else if (arg0.getID() == WindowEvent.WINDOW_CLOSED && !this.lastWidgetClicked.equals("")){
			this.structure.addCloseWindowEvent(this.lastWidgetClicked, this.hierarchy.findWidgetId((Component)arg0.getSource()));
		}else if (arg0.getID() == MouseEvent.MOUSE_PRESSED){
		
			String s = this.hierarchy.findWidgetId((Component) arg0.getSource());
			if(s != null){
				this.lastWidgetClicked = s;
			}
			System.out.println("Mouse Clicked at " + this.lastWidgetClicked);
		}
		
	}
	
}


/*

*/